package org.sanguine.plugin.timetracker;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.*;

import javax.swing.*;

import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.sanguine.plugin.timetracker.events.UpdateProjectsEvent;
import org.sanguine.plugin.timetracker.pojo.AccountInfo;
import org.sanguine.plugin.timetracker.pojo.Project;
import org.sanguine.plugin.timetracker.providers.TrackerServiceProvider;
import org.sanguine.plugin.timetracker.providers.TrackerServiceProviderFactory;

import com.intellij.ide.util.PropertiesComponent;
import com.intellij.openapi.components.ApplicationComponent;
import com.intellij.openapi.options.Configurable;
import com.intellij.openapi.options.ConfigurationException;

/**
 * Time Tracker IDEA plugin component
 */

public class TimerComponent implements ApplicationComponent, Configurable {

    private final static String PROVIDER_NAME = "HARVEST";

    private String providerName;
    private String username;
    private char[] password;
    private String uri;
    private TimerConfigForm configForm;
    private volatile boolean authenticated;
    private volatile TrackerServiceProvider trackerServiceProvider;
    private List<ActionListener> listeners = new CopyOnWriteArrayList<>();
    private MainDialogPanel mainPanel;
    private ScheduledExecutorService scheduledExecutorService = new ScheduledThreadPoolExecutor(5);
    private ExecutorService eventExecutorService = new ForkJoinPool();

    String getProviderName() {
        return providerName;
    }

    void setProviderName(String providerName) {
        this.providerName = providerName;
    }

    String getUsername() {
        return username;
    }

    void setUsername(String username) {
        this.username = username;
    }

    char[] getPassword() {
        return password;
    }

    void setPassword(char[] password) {
        this.password = password;
    }

    String getUri() {
        return uri;
    }

    void setUri(String uri) {
        this.uri = uri;
    }

    public TimerComponent() {

    }

    @Override
    public void initComponent() {
        username = PropertiesComponent.getInstance().getValue("TIME_TRACKER_USERNAME");
        uri = PropertiesComponent.getInstance().getValue("TIME_TRACKER_URI", "https://");
        password = PropertiesComponent.getInstance().getValue("TIME_TRACKER_PASSWORD", "").toCharArray();
        providerName = PropertiesComponent.getInstance().getValue("TIME_TRACKER_PROVIDER_NAME", PROVIDER_NAME);

        mainPanel = new MainDialogPanel(this);
        addActionListener(mainPanel);

        trackerServiceProvider = TrackerServiceProviderFactory.getProvider(providerName);

        if (uri != null && !uri.isEmpty()
                && username != null && !username.isEmpty()
                && password != null && password.length > 0) {

            try {
                authenticated = initializeAndGetAccountInfo(uri, username, password, trackerServiceProvider) != null;
            } catch (IllegalArgumentException e) {
                e.printStackTrace();
                uri = null;
                authenticated = false;
            }
        }

        scheduledExecutorService.scheduleWithFixedDelay(new Runnable() {
            @Override
            public void run() {
                updateProjects();
            }
        }, 0, 10, TimeUnit.SECONDS);
        scheduledExecutorService.scheduleWithFixedDelay(new Runnable() {
            @Override
            public void run() {
                updateStatus();
            }
        }, 0, 30, TimeUnit.SECONDS);

    }

    private void updateStatus() {
        /*if (isAuthenticated()) { org.sanguine.plugin.timetracker.pojo.Timer activeTimer =
         * trackerServiceProvider.getActiveTimer(); if (activeTimer != null) { TimerStartedEvent event = new
         * TimerStartedEvent(this, 0, "", activeTimer.hours); processEvent(event); } else { TimerStoppedEvent event =
         * new TimerStoppedEvent(this, 0, "", 0.0f); processEvent(event); } } */
    }

    private void updateProjects() {
        if (isAuthenticated()) {
            List<Project> projects = trackerServiceProvider.getProjects();
            UpdateProjectsEvent event = new UpdateProjectsEvent(this, 0, "", projects);
            processEvent(event);
        }
    }

    @Override
    public void disposeComponent() {
        trackerServiceProvider = null;
        configForm = null;
        Arrays.fill(password, '\b');
        password = null;
        uri = null;
        username = null;
        providerName = null;
        mainPanel = null;
        scheduledExecutorService.shutdownNow();
        scheduledExecutorService = null;
        eventExecutorService.shutdownNow();
        eventExecutorService = null;
    }

    @NotNull
    @Override
    public String getComponentName() {
        return "TimerComponent";
    }

    @Nls
    @Override
    public String getDisplayName() {
        return "Time tracker";
    }

    @Nullable
    @Override
    public String getHelpTopic() {
        return null;
    }

    @Nullable
    @Override
    public JComponent createComponent() {
        if (configForm == null)
            configForm = new TimerConfigForm(this);
        return configForm.$$$getRootComponent$$$();
    }

    @Override
    public boolean isModified() {
        return configForm != null && configForm.isModified(this);
    }

    @Override
    public void apply() throws ConfigurationException {
        if (configForm != null) {
            configForm.getData(this);
            new Thread() {
                @Override
                public void run() {
                    TrackerServiceProvider provider = TrackerServiceProviderFactory.getProvider(providerName);
                    try {
                        AccountInfo info = initializeAndGetAccountInfo(uri, username, password, provider);
                        authenticated = (info != null);
                        trackerServiceProvider = provider;

                        PropertiesComponent.getInstance().setValue("TIME_TRACKER_USERNAME", username);
                        PropertiesComponent.getInstance().setValue("TIME_TRACKER_URI", uri);
                        PropertiesComponent.getInstance().setValue("TIME_TRACKER_PASSWORD", new String(password));
                        PropertiesComponent.getInstance().setValue("TIME_TRACKER_PROVIDER_NAME", providerName);
                    } catch (IllegalArgumentException e) {
                        e.printStackTrace();
                    }
                }
            }.start();
        }
    }

    @Override
    public void reset() {
        if (configForm != null)
            configForm.setData(this);
    }

    @Override
    public void disposeUIResources() {
        configForm = null;
    }

    AccountInfo checkCredential(String endPoint, String username, char[] password) {
        TrackerServiceProvider provider = TrackerServiceProviderFactory.getProvider(providerName);
        return initializeAndGetAccountInfo(endPoint, username, password, provider);
    }

    private AccountInfo initializeAndGetAccountInfo(String endPoint, String username, char[] password, TrackerServiceProvider provider) {
        AccountInfo result = null;

        if (provider.initialize(endPoint, username, password)) {
            result = provider.getAccountInfo();
        }

        return result;
    }

    boolean isAuthenticated() {
        return authenticated && getTrackerServiceProvider() != null;
    }

    TrackerServiceProvider getTrackerServiceProvider() {
        return trackerServiceProvider;
    }

    void addActionListener(ActionListener listener) {
        if (listener != null) listeners.add(listener);
    }

    void processEvent(final ActionEvent e) {
        if (e != null) {
            for (final ActionListener listener : listeners) {
                eventExecutorService.execute(new Runnable() {
                    @Override
                    public void run() {
                        listener.actionPerformed(e);
                    }
                });
            }
        }
    }
}
